package helper
import nci60.administration.Params

/**
 * GRAILS <a href="http://grails.org/doc/latest/guide/single.html#services" target="_blank">Services</a>
 * This service contains some commonly shared methods/closures, which fullfill simple tasks, 
 * like merging the elements of a list to a String with a given separator.
 *
 * @autor <a href="mailto:Florian.J.Auer@googlemail.com">Florian J. Auer</a>
*/
class HelperService {

    /**
    * GRAILS <a href="http://grails.org/doc/latest/guide/single.html#declarativeTransactions" target="_blank">Transactional</a>
    * Disables automatic transaction demarcation.
    *
    * @autor <a href="mailto:Florian.J.Auer@googlemail.com">Florian J. Auer</a>
    */
    static transactional = false


    /**
     * Merges a the elements of a List to a String by using the given separator<p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              List
     * </td><td class="z">
     *              list
     * </td><td>        List of elements
     * </td></tr>
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *              separator
     * </td><td>        Used separator for joining the list
     * </td></tr>
     * </table><p />
     * <p />
     * 
     * Return: <p />
     * 
     * <table class="custom-table">
     * <tr><td class="z">
     *              String
     * </td><td>        String of joined elements
     * </td></tr>
     * </table>
     */
    String mergeList(List list, String separator){
        def result = ''
        list.eachWithIndex{element,index->
            if(index==0){
                result = element
            }else{
                result = result+separator+element
            }
        }
        return result
    }

    
    /**
     * Returns the default parameters ({@link nci60.administration.Params} class) stored in the database as (chained) Map <p />
     * <p />
     *  
     * Return: <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Map
     * </td><td>        Joined (linked) Map of the key/value paires of the {@link nci60.administration.Params} class <p />
     *                  e.g. the pairs key="some.sample" value="one" and key="some.other" value="two" 
     *                  are joined in the map: 
     *                  <table>
     *                  <tr><td>.some   </td><td>.sample</td><td>=one</td></tr>
     *                  <tr><td>        </td><td>.other </td><td>=two</td></tr>
     *                  </table>
     * </td></tr>
     * </table>
     */
    Map getParameters() {
        def result = [:]
        Params.findAll().each{param->
            def tmp = result
            def key = param.paramkey
            def path = key.split('\\.')
            path.eachWithIndex{k,i->
                def input = (i<(path.size()-1))?[:]:param.paramvalue
                if(!tmp.containsKey(k)){tmp.put(k, input)}
                tmp = tmp[k]
            }
        }
        return result
    }

    
    /**
     * Returns the default parameters ({@link nci60.administration.Params} class) stored in the database as (chained) Map. <p />
     * All parameters must start with the specified string, followed by a point
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *              start
     * </td><td>        The key must start with this String, e.g. "some.sample"
     * </td></tr>
     * </table><p />
     * <p />
     *  
     * Return: <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Map
     * </td><td>        Joined (linked) Map of the key/value paires of the {@link nci60.administration.Params} class <p />
     *                  e.g. the pairs key="and.some.sample" value="one", key="blubb.some.other" value="two", key="and.some.other" value="three" , and start="and"
     *                  are joined in the map: 
     *                  <table>
     *                  <tr><td>.and   </td><td>.some   </td><td>.sample</td><td>=one</td></tr>
     *                  <tr><td>       </td><td>        </td><td>.other </td><td>=three</td></tr>
     *                  </table>
     * </td></tr>
     * </table>
     */
    Map getParameters(String start) {
        def result = [:]
        Params.findAllByParamkeyLike(start+'%').each{param->
            def tmp = result
            def key = param.paramkey
            def path = key.split('\\.')
            path.eachWithIndex{k,i->
                def input = (i<(path.size()-1))?[:]:param.paramvalue
                if(!tmp.containsKey(k)){tmp.put(k, input)}
                tmp = tmp[k]
            }
        }
        return result
    }

    
    /**
     * Saves a Map as key/value paires in the database ({@link nci60.administration.Params} class).<p />
     * If the database allready contains the key, the value is updated/overwritten with the new value.
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Map
     * </td><td class="z">
     *              params
     * </td><td>        (linked) Map to be stored in the database <p />
     *                  e.g. the Map
     *                  <table>
     *                  <tr><td>.and   </td><td>.some   </td><td>.sample</td><td>=one</td></tr>
     *                  <tr><td>       </td><td>        </td><td>.other </td><td>=two</td></tr>
     *                  </table>
     *                  is stored as the pairs <p />
     *                  key="and.some.sample" value="one" <p />
     *                  key="and.some.other" value="two"
     * </td></tr>
     * </table>
     * 
     */
    void setParameters(Map params){
        setParameters(params, '', true)
    }
    
    
    /**
     * Saves a Map as key/value paires in the database ({@link nci60.administration.Params} class).<p />
     * This method uses an recursive call of the method {@link #setParameters(Map, boolean)}.
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Map
     * </td><td class="z">
     *              params
     * </td><td>        (linked) Map to be stored in the database <p />
     *                  e.g. the Map
     *                  <table>
     *                  <tr><td>.and   </td><td>.some   </td><td>.sample</td><td>=one</td></tr>
     *                  <tr><td>       </td><td>        </td><td>.other </td><td>=two</td></tr>
     *                  </table>
     *                  is stored as the pairs <p />
     *                  key="and.some.sample" value="one" <p />
     *                  key="and.some.other" value="two"
     * </td></tr>
     * <tr><td class="z">
     *              boolean
     * </td><td class="z">
     *              override
     * </td><td>        If "true", the value is updated/overwritten with the new value, if key is allready in the database.
     * </td></tr>
     * </table>
     * 
     */
    void setParameters(Map params, boolean override){
        setParameters(params, '', override)
    }
    
    /**
     * Saves a Map as key/value paires in the database ({@link nci60.administration.Params} class).<p />
     * This method is recursive.
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Map
     * </td><td class="z">
     *              params
     * </td><td>        (linked) Map to be stored in the database <p />
     *                  e.g. the Map
     *                  <table>
     *                  <tr><td>.and   </td><td>.some   </td><td>.sample</td><td>=one</td></tr>
     *                  <tr><td>       </td><td>        </td><td>.other </td><td>=two</td></tr>
     *                  </table>
     *                  is stored as the pairs <p />
     *                  key="and.some.sample" value="one" <p />
     *                  key="and.some.other" value="two"
     * </td></tr>
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *              prefix
     * </td><td>        A String that is put before each key
     * </td></tr>
     * <tr><td class="z">
     *              boolean
     * </td><td class="z">
     *              override
     * </td><td>        If "true", the value is updated/overwritten with the new value, if key is allready in the database.
     * </td></tr>
     * </table>
     * 
     */
    void setParameters(Map params, String prefix, boolean override){
        params.each{k,v->
            if(v instanceof Map){
                setParameters(v, prefix+k+'.', override)
            }else{
                saveParameter(prefix+k, v.toString(), override)
            }
        }
    }
    
    /**
     * Saves a key/value pair in the database ({@link nci60.administration.Params} class).<p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *              key
     * </td><td>        key to be saved
     * </td></tr>
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *              value
     * </td><td>        value to be saved
     * </td></tr>
     * <tr><td class="z">
     *              boolean
     * </td><td class="z">
     *              override
     * </td><td>        If "true", the value is updated/overwritten with the new value, if key is allready in the database.
     * </td></tr>
     * </table>
     * 
     */
    void saveParameter(String key, String value, boolean override){
        def para = Params.findByParamkey(key)
        if(para==null){
            para = new Params(paramkey:key, paramvalue:value)
        }else if(override){
            para.paramvalue = value
        }
        if (!para.save(flush:true)) {
            para.errors.each {println it}
        }
    } 
    
    
    /**
     * Saves a key/value pair in the database ({@link nci60.administration.Params} class).<p />
     * Overwrites the value, if allready in the database.
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *              key
     * </td><td>        key to be saved
     * </td></tr>
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *              value
     * </td><td>        value to be saved
     * </td></tr>
     * </table>
     * 
     */
    void saveParameter(String key, String value){
        saveParameter(key, value, true)
    }    
    
    
    /**
     * Simply prints a (linked) Map<p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Map
     * </td><td class="z">
     *              params
     * </td><td>        (linked) Map
     * </td></tr>
     * </table>
     * 
     */
    void printMap(Map params){
        printMap(params, '')
    }
    
    
    /**
     * Simply prints a (linked) Map<p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Map
     * </td><td class="z">
     *              params
     * </td><td>        (linked) Map
     * </td></tr>
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *              prefix
     * </td><td>        puts the prefix before the Map
     * </td></tr>
     * </table>
     * 
     */
    void printMap(Map params, String prefix){
        params.each{k,v->
            if(v instanceof Map){
                print prefix+k
                printMap(v, prefix+'\t')
            }else{
                println prefix+k+'\t'+v
            }
        }
    }
}
